﻿using Core.Maths;
using Core.Rendering.Buffer;
using Core.Rendering.Geometry;
using OpenTK.Mathematics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Core.Rendering.Resources
{
    public class Mesh : IDisposable
    {
        public string MeshName { get; }
        public int VertexCount { get; }
        public int IndexCount { get; }
        public int MaterialIndex { get; }
        private List<Vertex> vertices;
        public Sphere BoundingSphere { get; private set; }
        private IndexBufferObject? _indexBufferObject = null;
        private VertexBufferObject _vertexBufferObject;
        private VertexArrayObject _vertexArrayObject;
        public Mesh(List<Vertex> vertices, List<uint> indices, int materialIndex, string meshName)
        {
            VertexCount = vertices.Count;
            IndexCount = indices.Count;
            MaterialIndex = materialIndex;
            MeshName = meshName;
            CreateBuffer(vertices, indices);
            CreateBoundingSphere(vertices);
        }
        private void CreateBoundingSphere(List<Vertex> vertices)
        {
            float minX = float.MaxValue;
            float minY = float.MaxValue;
            float minZ = float.MaxValue;
            float maxX = float.MinValue;
            float maxY = float.MinValue;
            float maxZ = float.MinValue;
            foreach(Vertex vertex in vertices)
            {
                minX = MathHelper.Min(minX,vertex.Position.X);
                minY = MathHelper.Min(minY, vertex.Position.Y);
                minZ = MathHelper.Min(minZ, vertex.Position.Z);
                maxX = MathHelper.Max(minX, vertex.Position.X);
                maxY = MathHelper.Max(minY, vertex.Position.Y);
                maxZ = MathHelper.Max(minZ, vertex.Position.Z);
            }
            Vector3 position = new Vector3(minX + maxX, minY + maxY, minZ + maxZ) / 2;
            float radius = MathHelper.Max(
                Vector3.Distance(position, new Vector3(minX, minY, minZ)),
                Vector3.Distance(position, new Vector3(maxX, maxY, maxZ)));
            BoundingSphere = new Sphere(position, radius);
        }
        private void CreateBuffer(List<Vertex> vertices, List<uint> indices)
        {
            this.vertices = vertices;
            List<float> vertexData = new List<float>();
            foreach (Vertex vertex in vertices)
            {
                vertexData.Add(vertex.Position.X);
                vertexData.Add(vertex.Position.Y);
                vertexData.Add(vertex.Position.Z);
                vertexData.Add(vertex.TexCoords.X);
                vertexData.Add(vertex.TexCoords.Y);
                vertexData.Add(vertex.Normal.X);
                vertexData.Add(vertex.Normal.Y);
                vertexData.Add(vertex.Normal.Z);
                vertexData.Add(vertex.Tangent.X);
                vertexData.Add(vertex.Tangent.Y);
                vertexData.Add(vertex.Tangent.Z);
                vertexData.Add(vertex.BiTangent.X);
                vertexData.Add(vertex.BiTangent.Y);
                vertexData.Add(vertex.BiTangent.Z);
            }
            _vertexBufferObject = new VertexBufferObject([.. vertexData]);
            VertexBufferLayout layout = new VertexBufferLayout();
            layout.AddELement(new VertexBufferLayoutElement(0, 3),
                new VertexBufferLayoutElement(1, 2),
                new VertexBufferLayoutElement(2, 3),
                new VertexBufferLayoutElement(3, 3),
                new VertexBufferLayoutElement(4, 3));
            _vertexBufferObject.AddLayout(layout);
            if (indices.Count > 3)
            {
                _indexBufferObject = new IndexBufferObject([.. indices]);
            }
            _vertexArrayObject = new VertexArrayObject(_indexBufferObject, _vertexBufferObject);
        }
        public void Bind()
        {
            _vertexArrayObject.Bind();
        }
        public static void Unbind()
        {
            VertexArrayObject.UnBind();
        }
        public void Dispose()
        {
            _vertexArrayObject?.Dispose();
            _indexBufferObject?.Dispose();
            _vertexBufferObject?.Dispose();
        }
    }
}
